﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.IO;
using AcctrueWMS.Foundation.Web;
using System.Xml.Linq;
using System.Data;
using AcctrueWMS.Foundation.Common;
using System.Web;
using System.Xml;
using System.Text.RegularExpressions;
using AcctrueWMS.Foundation.Render;
using AcctrueWMS.Foundation.Common.Exception;

namespace AcctrueWMS.Foundation.Data
{
    /// <summary>
    /// 
    /// </summary>
    public class DataModelManager : IDataModelManager
    {

        #region IDataModelManager 成员

        /// <summary>
        /// 查询符合条件的模型对象
        /// </summary>
        /// <param name="path">文件夹路径</param>
        /// <param name="modelName">模型名称</param>
        /// <param name="modelphysicalName">模型类型</param>
        /// <returns></returns>
        public System.Collections.ObjectModel.Collection<DataModel> SelectModels(string path, string modelName, string modelphysicalName)
        {
            IDataModelLoader loader = new DataModelLoder();
            Collection<DataModel> dataModels = loader.LoadModels(path);
            Collection<DataModel> dataModelsSelected = new Collection<DataModel>();

            //填入模型名称并且填入类型
            if (modelName != string.Empty && modelphysicalName != string.Empty)
            {
                var res = from dataModel in dataModels where dataModel.Name == modelName  select dataModel;
                foreach (DataModel dataModel in res)
                {
                    dataModelsSelected.Add(dataModel);
                }
            }
            //都不填
            else
            {
                dataModelsSelected = dataModels;
            }
            return dataModelsSelected;
        }

        /// <summary>
        /// 删除一组DataModel文件
        /// </summary>
        /// <param name="fileNames">文件名</param>
        /// <returns>删除是否成功</returns>
        public bool DelXDocument(List<string> fileNames, string path)
        {
            bool del = true;
            foreach (string fileName in fileNames)
            {
                string xmlPath = path + @"\" + fileName + ".xml";
                del = File.Exists(xmlPath);
                try
                {
                    File.Delete(xmlPath);
                }
                catch
                {
                    return false;
                }
            }
            return del;
        }

        public bool DelColumn(string path, string name)
        {
            XDocument doc = XDocument.Load(path);
            var res = from column in doc.Root.Elements()
                      where column.Attribute("name").Value == name
                      select column;
            if (res.Count() == 0)
            {
                return false;
            }
            try
            {
                res.First().Remove();
                doc.Save(path);
            }
            catch (Exception ex)
            {
                return false;
            }
            return true;
        }
        public List<string> GetColumnNames(string path)
        {
            XDocument doc = XDocument.Load(path);
            IEnumerable<XElement> elts = doc.Root.Element("Columns").Elements();
            List<string> columnNames = new List<string>();
            foreach (XElement elt in elts)
            {
                columnNames.Add(elt.Attribute("name").Value);
            }
            return columnNames;
        }
        public bool AddColumns(string path, List<DataModelColumn> columns)
        {

            XDocument doc = XDocument.Load(path);
            try
            {
                foreach (DataModelColumn column in columns)
                {
                    doc.Root.Element("Columns").Add(new XElement("Column",
                            new XAttribute("name", column.Name),
                            new XAttribute("displayName", column.DisplayName),
                            new XAttribute("columnType", column.ColumnType),
                            new XAttribute("lengthDef", column.Length))
                            );
                }
            }
            catch
            {
                return false;
            }
            doc.Save(path);
            return true;
        }
        public bool DeleteColumns(string path, List<string> names)
        {
            XDocument doc = XDocument.Load(path);
            IEnumerable<XElement> elts = doc.Root.Element("Columns").Elements();
            try
            {
                var res = from elt in elts where names.Contains(elt.Attribute("name").Value) select elt;
                foreach (XElement elt in res)
                {
                    elt.Remove();
                }
            }
            catch
            {
                return false;
            }
            doc.Save(path);
            return true;
        }
        public void UpdateXMLColumns(string path, DataTable table)
        {

            List<string> tableColumnNames = new List<string>();
            List<string> modelColumnNames = new List<string>();
            List<string> deleteNames = new List<string>();
            List<string> addNames = new List<string>();

            foreach (DataRow row in table.Rows)
            {
                tableColumnNames.Add(row["Name"].ToString());
            }
            XDocument doc = XDocument.Load(path);
            IEnumerable<XElement> elts = doc.Root.Element("Columns").Elements();
            foreach (XElement elt in elts)
            {
                modelColumnNames.Add(elt.Attribute("name").Value);
            }
            List<string> columnNames = GetColumnNames(path);
            List<DataModelColumn> columns = new List<DataModelColumn>();
            DataModelColumn column = new DataModelColumn();
            foreach (string tableColumnName in tableColumnNames)
            {
                if (!columnNames.Contains(tableColumnName))
                {//添加
                    addNames.Add(tableColumnName);
                    foreach (DataRow row in table.Rows)
                    {
                        if (row["Name"].ToString() == tableColumnName)
                        {
                            column.Name = tableColumnName;
                            column.DisplayName = row["DisplayName"].ToString();
                            column.ColumnType = TypeConverter.Converter.DirType[row["ColumnType"].ToString()];
                            column.Length = row["Length"].ToString();
                            columns.Add(column);
                        }
                    }
                }
            }
            foreach (string columnName in columnNames)
            {
                if (!tableColumnNames.Contains(columnName))
                { //删除
                    deleteNames.Add(columnName);
                }
            }
            AddColumns(path, columns);
            DeleteColumns(path, deleteNames);

        }
        public DataTable AllXMLLoad(string path, params string[] bound)//, string boundDisplayName, string boundColumnType, string boundLength)
        {
            System.IO.DirectoryInfo dirGetXMLs = new System.IO.DirectoryInfo(path);
            FileInfo[] xmls = dirGetXMLs.GetFiles("*.xml");
            DataTable table = new DataTable();
            table.Columns.Add(bound[0]);
            table.Columns.Add(bound[1]);
            table.Columns.Add(bound[2]);
            table.Columns.Add("Path");
            foreach (FileInfo xml in xmls)
            {
                string xmlPath = path + @"\" + xml.Name; 
                XDocument doc = XDocument.Load(xmlPath);
                string name = doc.Root.Attribute("name") == null ? string.Empty : doc.Root.Attribute("name").Value;
                string displayName = doc.Root.Attribute("displayName") == null ? string.Empty : doc.Root.Attribute("displayName").Value;
                string physicalName = doc.Root.Attribute("physicalName") == null ? string.Empty : doc.Root.Attribute("physicalName").Value;
                DataRow row = table.NewRow();

                row[bound[0]] = name;
                row[bound[1]] = displayName;
                row[bound[2]] = physicalName;
                row["Path"] = xmlPath;
                table.Rows.Add(row);
            }
            return table;
        }

        #endregion
        private IDataModelContext context = Runtime.Context;
        TemplateManager templateManager = new TemplateManager();
        log4net.ILog logger = LoggerManager.GetLogger("logerror");

        #region oldCode
        ///// <summary>
        ///// 处理查询字符串
        ///// </summary>
        ///// <param name="model"></param>
        ///// <param name="queryStr"></param>
        ///// <returns></returns>
        //public StringBuilder ProcessQueryStr(DataModel model, string queryStr)
        //{
        //    StringBuilder sb = new StringBuilder("");
        //    queryStr = HttpUtility.UrlDecode(queryStr);
        //    if (queryStr != "undefined" && queryStr.Trim().Length != 0)
        //        if (model != null && queryStr != "undefined" && queryStr.Trim().Length != 0)
        //        {
        //            string[] querylist = queryStr.Split('&');

        //            foreach (string s in querylist)
        //            {
        //                if (!s.Trim().EndsWith("="))
        //                {
        //                    ///如果有con查询条件,直接传过去
        //                    if (s.StartsWith("con="))
        //                    {
        //                        sb.Append(s.Remove(0, 4)).Insert(0, " and ");
        //                        continue;
        //                    }
        //                    string[] keyvalues = s.Split('=');

        //                    //判断控件类型
        //                    string displayType = model.Columns[keyvalues[0]].DisplayType;
        //                    DataModelColumnDisplayTypeEnum displayTypeConvert;
        //                    if (Enum.TryParse<DataModelColumnDisplayTypeEnum>(displayType, out displayTypeConvert))
        //                    {
        //                        switch (displayTypeConvert)
        //                        {
        //                            case DataModelColumnDisplayTypeEnum.Text:
        //                            case DataModelColumnDisplayTypeEnum.TextBox:
        //                            case DataModelColumnDisplayTypeEnum.FocusedTextBox:
        //                                if (sb.Length != 0)
        //                                    sb.Append(" and " + model.Name + "." + keyvalues[0] + " like '%" + HttpUtility.UrlDecode(keyvalues[1]).Trim() + "%'");
        //                                else
        //                                    sb.Append("" + model.Name + "." + keyvalues[0] + " like '%" + HttpUtility.UrlDecode(keyvalues[1]).Trim() + "%'");
        //                                break;
        //                            case DataModelColumnDisplayTypeEnum.Radio:
        //                                if (sb.Length != 0)
        //                                    sb.Append(" and " + model.Name + "." + keyvalues[0] + " ='" + HttpUtility.UrlDecode(keyvalues[1]).Trim() + "'");
        //                                else
        //                                    sb.Append("" + model.Name + "." + keyvalues[0] + " ='" + HttpUtility.UrlDecode(keyvalues[1]).Trim() + "'");
        //                                break;
        //                            case DataModelColumnDisplayTypeEnum.CheckBox:
        //                                break;
        //                            case DataModelColumnDisplayTypeEnum.Calendar:
        //                                break;
        //                            case DataModelColumnDisplayTypeEnum.Label:
        //                                break;
        //                            case DataModelColumnDisplayTypeEnum.Tree:
        //                                break;
        //                            case DataModelColumnDisplayTypeEnum.DropDownList:
        //                                if (sb.Length != 0)
        //                                {
        //                                    sb.Append(" and " + model.Name + "." + keyvalues[0] + "=" + HttpUtility.UrlDecode(keyvalues[1]) + "");
        //                                }
        //                                else
        //                                {
        //                                    sb.Append(" " + model.Name + "." + keyvalues[0] + "=" + HttpUtility.UrlDecode(keyvalues[1]) + "");
        //                                }
        //                                break;
        //                        }
        //                    }
        //                }
        //            }

        //        }
        //    return sb;

        //}
        
        ///// <summary>
        ///// 查询中记录数,并计算总页数
        ///// </summary>
        ///// <param name="modelName"></param>
        ///// <param name="queryStr"></param>
        ///// <param name="pageSize"></param>
        ///// <returns></returns>
        //public string GetTotalInfo(string modelName, string queryStr, string pageSize)
        //{
        //    try
        //    {
        //        string totalinfo = string.Empty;
        //        DataModel model = new DataModel(context, modelName);
        //        context.Select(model, "    " + ProcessQueryStr(model, queryStr).ToString());

        //        string rtnKeyValueExpress = string.Empty;
        //        ProcessModel(OptTypeEnum.List, model, ref rtnKeyValueExpress, DataModelEventNameKeys.BeforeLoad);
        //        ProcessModel(OptTypeEnum.List, model, ref rtnKeyValueExpress, DataModelEventNameKeys.Load);
        //        ProcessModel(OptTypeEnum.List, model, ref rtnKeyValueExpress, DataModelEventNameKeys.AfterLoad);
        //        totalinfo = model.Rows.Count + "$" + CaculatePageCount(Convert.ToInt32(pageSize), model);
        //        return totalinfo;
        //    }
        //    catch (Exception ex)
        //    {
        //        logger.Error(ex.Message);
        //        return string.Empty;
        //    }
        //}
        #endregion

        /// <summary>
        /// 获取Model类型
        /// </summary>
        /// <param name="opttype"></param>
        /// <returns></returns>
        private int GetOptType(string opttype)
        {
            switch (opttype)
            {
                case "0":
                    return 0;

                case "1":
                case "2":
                    return 1;
                default:
                    return 0;
            }
        }
       
        /// <summary>
        /// 计算总页数
        /// </summary>
        /// <param name="pageSize"></param>
        /// <param name="model"></param>
        /// <returns></returns>
        private static int CaculatePageCount(int pageSize, DataModel model)
        {
            int totalpage = 0;
            if (model.Rows.Count % pageSize == 0)
                totalpage = model.Rows.Count / pageSize;
            else
                totalpage = model.Rows.Count / pageSize + 1;
            return totalpage;
        }
        /// <summary>
        /// 默认保存方法,如果有自己特殊的save方法,可在XML或URI处添加,也可在模板中指定。将顺序执行
        /// BeforeSave,Save,AfterSave方法；如果其中一个方法返回值为bool，则标志下一步骤是否执行；
        /// 如果返回值为DataModel，则下一步骤使用上一步骤结果。
        /// </summary>
        /// <param name="modelName"></param>
        /// <param name="jsonStr"></param>
        /// <param name="opttype"></param>
        /// <returns></returns>
        public object SaveModel(string modelName, string jsonStr, string opttype)
        {
            try
            {
                HttpRequest request=HttpContext.Current.Request;
                string before = request.QueryString["beforeSave"];
                string save=request.QueryString["save"];
                string after = request.QueryString["afterSave"];

                int renderMode=int.Parse(opttype);
                DataModel model = new DataModel(context, modelName);
                JsonTableConverter.ConvertJsonToDataModel(jsonStr, model);
                
                object result=null;
                string eventExpr = null;

                if(!string.IsNullOrEmpty(before))
                   eventExpr=before;
                else
                    eventExpr=FindEventHandler(model,DataModelEventNameKeys.BeforeSave);
                if (!string.IsNullOrEmpty(eventExpr))
                {
                    result = ProcessEventExpr(model, before, renderMode);
                    if (result is bool && !(bool)result)
                        return result;
                    if (result is EventResult && ((EventResult)result).Result == false)
                        return ((EventResult)result).Msg;
                    //如果返回值是DataModel，则使用处理过的Model替换现有的
                    model = (result is DataModel) ? (result as DataModel) : model;
                }

                //处理保存事件
                if (!string.IsNullOrEmpty(save))
                    eventExpr=save;
                else
                    eventExpr=FindEventHandler(model,DataModelEventNameKeys.Save);

                if (!string.IsNullOrEmpty(eventExpr))
                    result = ProcessEventExpr(model, before, renderMode); 
                else//如果没有配置save事件，则使用默认方法
                    result = model.Save(renderMode.ToString());

                if (result is bool && !(bool)result)
                    return false;
                model = (result is DataModel) ? (result as DataModel) : model;

                //处理afterSave事件
                if (!string.IsNullOrEmpty(after))
                    eventExpr=after;
                else
                    eventExpr=FindEventHandler(model,DataModelEventNameKeys.AfterSave);
                if (!string.IsNullOrEmpty(after))
                {
                    object afterResult = ProcessEventExpr(model, after, renderMode);
                    if (afterResult is bool && !(bool)afterResult)
                        return afterResult;
                    if (afterResult is EventResult && ((EventResult)afterResult).Result == false)
                        return ((EventResult)afterResult).Msg;
                    model = (result is DataModel) ? (result as DataModel) : model;
                }
                return result;
            }
            catch (Exception ex)
            {
                logger.Error(ex.Message);
                throw ex;
            }         
        }

        private void IntoNextSaveEvent(object result)
        {

        }

        /// <summary>
        /// 寻找对应事件名称的相应表达式
        /// </summary>
        /// <param name="model"></param>
        /// <param name="eventName"></param>
        /// <returns>如没有对应表达式，返回空</returns>
        public string FindEventHandler(DataModel model, string eventName)
        {
            string modelEventexpr = string.Empty;
            if (model.DataModelEvents != null)
            {
                foreach (DataModelEventInfo item in model.DataModelEvents)
                {
                    if (item.EventName == eventName)
                        return item.EventExpr;
                }
            }
            return null;
        }

        /// <summary>
        /// 处理模型事件，并返回方法结果
        /// </summary>
        /// <param name="model">模型</param>
        /// <param name="optType">当前页面模式</param>
        /// <param name="eventName">事件名称</param>
        /// <returns></returns>
        public object ProcessEvent(DataModel model, int renderMode, string eventName)
        {
            string eventExpr = FindEventHandler(model, eventName);
            if(!string.IsNullOrEmpty(eventExpr))
                return ProcessEventExpr(model, eventExpr, renderMode);
            return null;
        }
        /// <summary>
        /// 处理事件
        /// </summary>
        /// <param name="model">数据模型</param>
        /// <param name="eventExpr">事件表达式</param>
        /// <param name="renderMode">展现模式</param>
        /// <returns>事件表达式定义的结果</returns>
        public object ProcessEventExpr(DataModel model,string eventExpr,int renderMode)
        {
            CommandAnalyser analyser = new CommandAnalyser(model, Convert.ToString((int)renderMode));
            string cmdText = string.Empty;
            if (CommandExprFactory.DetectType(eventExpr, out cmdText) == CommandExpressionTypeEnum.Sys)
            {
                analyser.ProcessParameter += ProcessParameter;
                return analyser.Eval(cmdText);
            }
            return null;
        }


        /// <summary>
        /// 处理参数,此处只处理dataTable和xmlDocument两种类型
        /// </summary>
        /// <param name="parameters">反射方法表达式</param>
        /// <param name="dt">Excel转变成的Dt</param>
        /// <param name="xmlDoc">定义的数据模型</param>
        private void ReplaceParameter(object[] parameters, DataTable dt, XmlDocument xmlDoc)
        {
            for (int i = 0; i < parameters.Length; i++)
            {
                if (parameters[i].ToString() == "{data}")
                {
                    parameters[i] = (object)dt;
                }
                if (parameters[i].ToString() == "{xml}")
                {
                    parameters[i] = (object)xmlDoc;
                }
            }
        }
        
        /// <summary>
        /// 功能：excel 导入 反射验证方法
        /// </summary>
        /// <param name="eventExpr">反射方法表达式</param>
        /// <param name="dt">Excel转变成的Dt</param>
        /// <param name="xmlDoc">定义的数据模型</param>
        /// <returns></returns>
        public object ProcessEventExpr(string eventExpr,DataTable dt,XmlDocument xmlDoc)
        {
            CommandAnalyser analyser = new CommandAnalyser(dt, xmlDoc);
            string cmdText = string.Empty;
            if (CommandExprFactory.DetectType(eventExpr, out cmdText) == CommandExpressionTypeEnum.Sys)
            {
                analyser.ReplaceParameter += ReplaceParameter;
                return analyser.Eval(cmdText);
            }
            return null;
        }

        /// <summary>
        /// 处理model,根据eventName可在加载前 加载时 加载后执行,或者在保存前 保存时 保存后执行
        /// </summary>
        /// <param name="opttype"></param>
        /// <param name="model"></param>
        /// <param name="resultmodel"></param>
        /// <param name="modelEventexpr"></param>
        public string ProcessModel(OptTypeEnum opttype, DataModel model, ref string keyValueExpress, string eventName)
        {
            string modelEventexpr = string.Empty;
            ///每次处理前先清空reusltModel

            //编辑页面时出错，暂时加上判断null值，以使界面正常走下去。lishuliang，06-13
            if (model.DataModelEvents != null)
            {
                foreach (DataModelEventInfo item in model.DataModelEvents)
                {
                    if (item.EventName == eventName)
                    {
                        modelEventexpr = item.EventExpr;
                        break;
                    }
                }
            }
            if (modelEventexpr.Length > 0)
            {
                CommandAnalyser analyser = new CommandAnalyser(model, Convert.ToString((int)opttype));
                string cmdText = string.Empty;
                if (CommandExprFactory.DetectType(modelEventexpr, out cmdText) == CommandExpressionTypeEnum.Sys)
                {
                    analyser.ProcessParameter += ProcessParameter;
                    keyValueExpress = (string)analyser.Eval(cmdText);
                }
            }
            else
            {
                if (eventName == DataModelEventNameKeys.Save)
                    keyValueExpress = model.Save(Convert.ToString((int)opttype));
            }
            return keyValueExpress;
        }
        /// <summary>
        /// 处理参数,此处只处理datamodel和opttype两种类型
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="model"></param>
        /// <param name="opttype"></param>
        private void ProcessParameter(object[] parameters, DataModel model, string opttype)
        {
            for (int i = 0; i < parameters.Length; i++)
            {
                if (parameters[i].ToString() == "{data}")
                {
                    parameters[i] = (object)model;
                }
                if (parameters[i].ToString() == "{opttype}")
                {
                    parameters[i] = (object)opttype;
                }
            }
        }

        /// <summary>
        /// 获取并绑定新的行
        /// </summary>
        /// <param name="modelName"></param>
        /// <param name="templateName"></param>
        /// <param name="detailModelName"></param>
        /// <returns></returns>
        public string GetNewRow(string modelName, string templateName, string detailModelName)
        {
            try
            {
                DataModel model = new DataModel(context, modelName);
                DataModel detailModel = null;
                for (int i = 0; i < model.Childs.Count; i++)
                {
                    if (model.Childs[i].Name == detailModelName)
                    {
                        detailModel = model.Childs[i];
                        break;
                    }

                }
                ModelTemplate template = templateManager.LoadTemplate(templateManager.GetTemplateFile(templateName));
                StringWriter sw = new StringWriter();
                ///如果找到Section段,则使用VTL绑定
                if (template.Sections.ContainsKey(detailModelName))
                {
                    context.Render.Render(template.Sections[detailModelName].InnerText, sw, detailModel);
                }
                return sw.ToString();
            }
            catch (Exception ex)
            {
                logger.Error(ex.Message);
                return string.Empty;
            }
        }

        /// <summary>
        /// 获取DataModel的主键值,如果有明细,则包含明细主键值
        /// </summary>
        /// <param name="modelName"></param>
        /// <returns></returns>
        public static DataModelKeyValue GetDataModelKeyValue(string modelName)
        {
            DataModel model = new DataModel(Runtime.Context, modelName);
            DataModelKeyValue modelKev = new DataModelKeyValue();
            modelKev.TableName = string.IsNullOrEmpty(model.physicalName) ? model.Name : model.physicalName.ToString();
            modelKev.ModelName = modelName;
            modelKev.KeyColumnName = model.KeyColumnName;
            modelKev.DetailKeyValues = new List<DataModelKeyValue>();
            #region 处理Child表
            for (int i = 0; i < model.Childs.Count; i++)
            {
                DataModelKeyValue detailkev = new DataModelKeyValue();
                detailkev.TableName = string.IsNullOrEmpty(model.Childs[i].physicalName) ? model.Childs[i].Name : model.Childs[i].physicalName.ToString();
                detailkev.ModelName = model.Childs[i].Name;
                string relationStr = "";
                for (int j = 0; j < model.Relations.Count; j++)
                {
                    if (model.Childs[i] == model.Relations[j].To)
                    {
                        relationStr = model.Relations[j].RelationExpression;
                        break;
                    }
                }
                detailkev.KeyColumnName = ForeignKeyAnalyser.GetForeightKey(relationStr, detailkev.ModelName);
                modelKev.DetailKeyValues.Add(detailkev);
            }
            #endregion
            #region 处理Brother表
            for (int i = 0; i < model.Brothers.Count; i++)
            {
                DataModelKeyValue detailkev = new DataModelKeyValue();
                detailkev.ModelName = model.Brothers[i].Name;
                detailkev.TableName=string.IsNullOrEmpty(model.Brothers[i].physicalName) ? model.Brothers[i].Name : model.Brothers[i].physicalName.ToString();
                string relationStr = "";
                for (int j = 0; j < model.Relations.Count; j++)
                {
                    if (model.Brothers[i] == model.Relations[j].To)
                    {
                        relationStr = model.Relations[j].RelationExpression;
                        break;
                    }
                }
                detailkev.KeyColumnName = ForeignKeyAnalyser.GetForeightKey(relationStr, detailkev.ModelName);
                modelKev.DetailKeyValues.Add(detailkev);

            }
            #endregion
            return modelKev;
        }
    }
}
